package com.lechi.iot.client.connect;

import com.lechi.iot.netty.channel.ITcpChannel;
import com.lechi.iot.netty.device.TcpDeviceChannel;
import com.lechi.iot.netty.utils.NettyCacheUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author yangguang
 */
@Slf4j
@Sharable
public abstract class AppClientConnect extends ChannelInboundHandlerAdapter implements TimerTask {

    private final Bootstrap boot;
    /**
     * 重连地址
     */
    private final Map<Timeout, SocketAddress> timeoutAddressMap = new HashMap<>();
    private int reconnectTime;
    private ChannelFuture future;
    private Timer timer;

    public AppClientConnect(Bootstrap boot, int reconnectTime) {
        this.boot = boot;
        this.reconnectTime = reconnectTime;
        this.timer = new HashedWheelTimer();
    }

    public ChannelFuture connect(SocketAddress socketAddress) throws Exception {
        future = boot.connect(socketAddress);
        future.addListener((ChannelFutureListener) future -> {
            if (!future.isSuccess()) {
                //连接失败，重连服务端，重连交给后端线程执行
                future.channel().eventLoop().schedule(() -> {
                    try {
                        connect(socketAddress);
                    } catch (Exception e) {
                        log.error("采集器重连服务端失败", e);
                    }
                }, reconnectTime, TimeUnit.MILLISECONDS);
            } else {
                afterSuccess();  //服务端连接成功
            }
        });
        //对通道关闭进行监听
        future.channel().closeFuture().sync();
        return future;
    }

    @Override
    public void run(Timeout timeout) throws Exception {
        if (!Objects.isNull(timeoutAddressMap.get(timeout))) {
            connect(timeoutAddressMap.get(timeout));
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        ctx.fireChannelActive();
        if (!Objects.isNull(channel.remoteAddress())) {
            ITcpChannel tcpChannel = new TcpDeviceChannel(channel);
            NettyCacheUtils.put(tcpChannel.name(), tcpChannel);
            log.error("采集器与上位机平台建立连接，服务端地址：{}", tcpChannel.name());
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.error("采集器与上位机平台断开连接，服务端地址：{}", ctx.channel().remoteAddress());
        ctx.fireChannelInactive();
        Channel channel = ctx.channel();
        if (Objects.nonNull(channel) && Objects.nonNull(channel.remoteAddress())) {
            String address = channel.remoteAddress().toString().substring(1);
            ITcpChannel tcpChannel = new TcpDeviceChannel(channel);
            if (Objects.nonNull(tcpChannel)) {
                NettyCacheUtils.remove(tcpChannel.name());
                if (address.contains(":")) {
                    String[] strArray = address.split(":");
                    Timeout timeout = timer.newTimeout(this, reconnectTime, TimeUnit.MILLISECONDS);
                    InetSocketAddress socketAddress = InetSocketAddress.createUnresolved(strArray[0], Integer.parseInt(strArray[1]));
                    timeoutAddressMap.put(timeout, socketAddress);
                }
            }
            //连接断开后的最后处理
            ctx.pipeline().remove(ctx.handler());
            ctx.deregister();
            ctx.close();
        }

    }

    public abstract void afterSuccess();
}
